package ca.uvic.group2.server;

import ca.uvic.group2.ServerFactory;
import ca.uvic.group2.api.*;
import ca.uvic.group2.api.exceptions.*;
import java.io.*;
import java.math.*;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.sql.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import org.apache.log4j.*;

abstract class DBTask implements Runnable {

    protected static Category debugLog = Logger.getInstance("DBTaskDebug");
    protected static Category perfLog = Logger.getInstance("DBTaskPerf");

    protected Exception error;
    protected int readQryCount;
    protected int writeQryCount;
    private long startTime;
    private long queueTime;
    private long exeTime;

    public Exception getError() {
        return error;
    }

    //
    // Creates and opens the DBConnection associated with this thread
    //
    private static ThreadLocal threadDbConnection = new ThreadLocal() {
            protected synchronized Object initialValue() {
                return DBConnectionPool.getInstance().acquireConnection();
            }
        };

    //
    // Gets the open connection associated with this thread
    //
    protected DBConnection2 getDbConnection() {
        debugLog.info("Getting Pooled DB Connection");
        return (DBConnection2)threadDbConnection.get();
    }

    static protected String getStockAccountTable(UserInfo user) {
        int num = 0;

//         if (username != null && username.length() > 0) {
//             int firstChar = (int)username.charAt(0);
//             if (firstChar >= 'a' && firstChar <= 'z') {
//                 num = (firstChar - 'a') + 1;
//             } else if (firstChar >= 'A' && firstChar <= 'Z') {
//                 num = (firstChar - 'A') + 1;
//             }
//         }

        num = user.userid % DBServerImpl2.NUM_STOCK_TABLES;
        return "stockaccounts" + num;
    }

    //
    // Task implementation
    //
    public DBTask()
    {
      this.readQryCount = 0;
      this.writeQryCount = 0;
      startTime = System.currentTimeMillis();
    }


    protected abstract void executeTask(DBConnection2 dbConnection) throws LoginException,
                                                                           CommException,
                                                                           RemoteException;
    public long getStartTime() {
        return startTime;
    }

    public long getQueueTime() {
        return queueTime;
    }

    public long getExeTime() {
        return exeTime;
    }


    public void run() {
        synchronized (this) {
            long localStartTime = System.currentTimeMillis();
            try {

                queueTime = localStartTime - startTime;
                DBConnection2 conn = getDbConnection();
                if (conn == null) {
                    throw new CommException("Fatal Error: No available DB Connections.");
                }
                executeTask(conn);
            } catch (Exception err) {
                debugLog.error("DB Task error", err);
                this.error = err;
            }
            exeTime = System.currentTimeMillis() - localStartTime;
            this.notifyAll();
        }
    }

    public int getReadQryCount()
    {
      return readQryCount;
    }

    public int getWriteQryCount()
    {
      return writeQryCount;
    }

    protected static int getResultSetSize(ResultSet resultSet) {
        int size = -1;

        long startTime = 0;
        if (perfLog.isDebugEnabled()) {
            startTime = System.currentTimeMillis();
        }

        try {
            resultSet.last();
            size = resultSet.getRow();
            resultSet.beforeFirst();
        } catch (SQLException err) {
            debugLog.error("getResultSetSize error", err);
            return size;
        }

        if (perfLog.isDebugEnabled()) {
            perfLog.debug("getRestulSetSize size:" + size + ", time:" + (startTime - System.currentTimeMillis()));
        }
        return size;
    }

    protected static BigDecimal roundForDB(float num) {
        double dbl = (double)num;
        BigDecimal bal = new BigDecimal(dbl);
        bal = bal.setScale(2, RoundingMode.HALF_EVEN);
        return bal;
    }

    protected static BigDecimal roundForDB(double num) {
        double dbl = (double)num;
        BigDecimal bal = new BigDecimal(dbl);
        bal = bal.setScale(2, RoundingMode.HALF_EVEN);
        return bal;
    }

    protected static BigDecimal roundForDB(Money num) {
        double dbl = num.doubleValue();
        BigDecimal bal = new BigDecimal(dbl);
        bal = bal.setScale(2, RoundingMode.HALF_EVEN);
        return bal;
    }

    public void throwIfLoginError() throws LoginException {
        if (this.error!= null) {
            if (this.error instanceof LoginException) {
                throw (LoginException)this.error;
            }
        }
    }

    public void throwIfError() throws CommException, RemoteException {
        if (this.error!= null) {
            if (this.error instanceof CommException) {
                throw (CommException)this.error;
            }
            if (this.error instanceof RemoteException) {
                throw (RemoteException)this.error;
            }
            if (this.error instanceof RuntimeException) {
                throw (RuntimeException)this.error;
            }
        }
    }

}

class LoginDBTask extends DBTask {
    private String userName;
    private UserInfo userInfo;
    private int userid;

    public LoginDBTask(String username) {
        super();
        this.userName = username;
    }

    public String getUsername() {
        return userName;
    }

    public UserInfo getUserInfo() {
        return userInfo;
    }

    public int getUserId() {
        return userid;
    }

    //
    // Executes the login task, save this result in userid and userInfo member variables,
    // then calls notifyAll to wake up the thread waiting for the task to complete.
    //
    protected void executeTask(DBConnection2 dbConnection) throws LoginException,
                                                                  CommException,
                                                                  RemoteException {

        debugLog.info("Running DBLogin Task...");
        error = null;
        userid = 0;
        userInfo = null;

        double balance = 0.01;
        ResultSet result = null, stock_result = null;
        //retrieve user balances from db - return userid
        String qry = "select u.userid, d.balance from users u, dollaraccounts d where u.name = '" + userName + "' and u.userid = d.userid";
        try {
            result = dbConnection.execQuery(qry);
            this.readQryCount++;
            int size = getResultSetSize(result);
            if ( size > 0 ) {
                result.first();
                userid = result.getInt("userid");
                try {
                    balance = NumberFormat.getCurrencyInstance().parse(result.getString("balance")).floatValue();
                } catch (ParseException e) {
                    balance = 0.0;
                    debugLog.warn("Balance parse error", e);
                }
            } else {
                userid = createUser(dbConnection, userName, "password");
                //set balance to default amount
                balance = 0.00;
            }

            //build the UserInfo for the given user and store it in the UserInfo hash table
            userInfo = new UserInfo(userid, userName, new Money(balance), new Money(0.0), new Money(0.0), null);

            //retrieve quote balances from the db and add them to the userinfo object
            String symbol = "";
            int s_balance = 0;
            String stock_qry = "SELECT stocksym, stock_balance FROM " +
                getStockAccountTable(userInfo) + " WHERE userid = '" + userid + "'";
            stock_result = dbConnection.execQuery(stock_qry);
            this.readQryCount++;
            int stk_size = getResultSetSize(result);

            stock_result.beforeFirst();
            while (stock_result.next()) {
                symbol = stock_result.getString("stocksym");
                s_balance = stock_result.getInt("stock_balance");
                userInfo.stocks.put(symbol, new StockRecord(symbol, s_balance));
            }
        } catch (SQLException sqle) {
            debugLog.warn("login: SQL Error!", sqle);
        }
    }

    public int createUser(DBConnection2 dbConnection, String username, String password) throws CommException, RemoteException {
        int userid = 0;

        String qry_update = "insert into users (name, password) values ('" + username + "','" + password + "')";
        qry_update += "; insert into dollaraccounts (userid, balance) select userid, '0.00'"
            + " from users where name = '" + username + "'";
        try {
            //insert the new user
            int updated = dbConnection.execUpdate(qry_update);
            this.writeQryCount++;
            //get the userid
            String qry = "select userid from users where name = '" + username + "'";

            ResultSet result = dbConnection.execQuery(qry);
            this.readQryCount++;
            result.first();
            userid = result.getInt("userid");
        } catch (SQLException sqle) {
            debugLog.warn("createUser: sql exception", sqle);
        }
        return userid;
    }
}

class AddBuyTriggerDBTask extends DBTask {

    private int userId;
    private UserInfo userInfo;
    private Trigger trigger;
    private int newTriggerId;

    public Trigger getTrigger() {
        return trigger;
    }

    public AddBuyTriggerDBTask(int userId, UserInfo userInfo, Trigger trigger, int newTriggerId) {
        super();
        this.userId = userId;
        this.userInfo = userInfo;
        this.trigger = trigger;
        this.newTriggerId = newTriggerId;
    }

    protected void executeTask(DBConnection2 dbConnection) throws LoginException,
                                                                  CommException,
                                                                  RemoteException
    {
        debugLog.info("addBuyTrigger: start");

        float dollarAmt = 0.00f;
        int stockAmt = 0, updateVal = 0;
        BigDecimal bal;
        String update_qry = "", select_qry = "";

        if (userInfo == null) {
            throw new CommException("No USer Info");
        }
        if (trigger == null) {
            throw new CommException("No Trigger");
        }
        userInfo.balance = userInfo.balance.subtract(trigger.amount);
        userInfo.triggerReserveBalance = userInfo.triggerReserveBalance.add(trigger.amount);
        trigger.id = newTriggerId;
        trigger.username = userInfo.username;
        bal = roundForDB(userInfo.balance.doubleValue());

        //this should be a buy trigger so the dollar amount is the valid trigger amount
        if (Trigger.BUY != trigger.kind)
            debugLog.info("addBuyTrigger: Wrong trigger type!");

        dollarAmt = trigger.amount;
        update_qry = "insert into triggers (username, kind, stocksym, stock_amount, stock_price, dollar_amount) "
                     + "values ('" + trigger.username + "','" + trigger.kind + "','" + trigger.stock + "','" + stockAmt + "','" + trigger.stockPrice + "','"
                     + dollarAmt + "')";
        update_qry += ";update dollaraccounts set balance = '" + bal + "' where userid = '" + userId + "'";
        debugLog.info("addBuyTrigger: qry" + update_qry);
        try {
            updateVal = dbConnection.execUpdate(update_qry);
            this.writeQryCount++;
            debugLog.info("addBuyTrigger: triggerid qry" + select_qry);

            select_qry = "select currval('triggers_triggerid_seq')";
            ResultSet result = dbConnection.execQuery(select_qry);
            this.readQryCount++;
            result.first();
            trigger.id = result.getInt("currval");
            System.out.println("triggerid returned by currval:" + trigger.id);


        } catch (SQLException sqle) {
            debugLog.warn("addBuyTrigger: SQLException", sqle);
            throw new CommException("addBuyTrigger: SQLException" + sqle);
        }
    }

}

class AddSellTriggerDBTask extends DBTask {

    private int userId;
    private UserInfo userInfo;
    private Trigger trigger;
    private int newTriggerId;

    public Trigger getTrigger() {
        return trigger;
    }

    public AddSellTriggerDBTask(int userId, UserInfo userInfo, Trigger trigger, int newTriggerId) {
        super();
        this.userId = userId;
        this.userInfo = userInfo;
        this.trigger = trigger;
        this.newTriggerId = newTriggerId;
    }

    protected void executeTask(DBConnection2 dbConnection) throws LoginException,
                                                                  CommException,
                                                                  RemoteException
    {
        debugLog.info("addSellTrigger: start");

        float dollarAmt = 0.00f;
        int stockAmt = 0, updateVal = 0;
        BigDecimal bal;

        // TODO: Write this to DB
        StockRecord record = (StockRecord)userInfo.stocks.get(trigger.stock);
        if (record != null) {
            record.amount -= trigger.amount;
            record.triggerReserveAmount += trigger.amount;
        }

        trigger.id = newTriggerId;
        trigger.username = userInfo.username;
        //====================================
        //this should be a buy trigger so the dollar amount is the valid trigger amount
        if (Trigger.SELL != trigger.kind)
            debugLog.info("addSellTrigger: Wrong trigger type!");

        stockAmt = (int)trigger.amount;


        StringBuffer update_qry = new StringBuffer();
        update_qry.append("INSERT INTO triggers (username, kind, stocksym, stock_amount, stock_price, dollar_amount) VALUES ('");
        update_qry.append(trigger.username);
        update_qry.append("','");
        update_qry.append(trigger.kind);
        update_qry.append("','");
        update_qry.append(trigger.stock);
        update_qry.append("','");
        update_qry.append(stockAmt);
        update_qry.append("','");
        update_qry.append(trigger.stockPrice);
        update_qry.append("','");
        update_qry.append(dollarAmt);
        update_qry.append("'); UPDATE ");
        update_qry.append(getStockAccountTable(userInfo));
        update_qry.append(" SET stock_balance = '");
        update_qry.append(record.amount);
        update_qry.append("' where userid = '");
        update_qry.append(userId);
        update_qry.append("' and stocksym = '");
        update_qry.append(record.stock);
        update_qry.append("';");
        String updateQueryString = update_qry.toString();
        debugLog.info("addSellTrigger: qry " + updateQueryString);
        try {
            updateVal = dbConnection.execUpdate(updateQueryString);
            this.writeQryCount++;

            String qry = "select currval('triggers_triggerid_seq')";
            debugLog.info("addSellTrigger: triggerid qry" + qry);
            ResultSet result = dbConnection.execQuery(qry);
            this.readQryCount++;
            result.first();
            trigger.id = result.getInt("currval");

        } catch (SQLException sqle) {
            debugLog.warn("addBuyTrigger: SQLException", sqle);
            throw new CommException("addBuyTrigger: SQLException" + sqle);
        }

        debugLog.info("addSellTrigger: finish");
    }

}

class CancelBuyTriggerDBTask extends DBTask {

    private int userId;
    private UserInfo userInfo;
    private Trigger trigger;

    public CancelBuyTriggerDBTask(int userId, UserInfo userInfo, Trigger trigger) {
        super();
        this.userId = userId;
        this.userInfo = userInfo;
        this.trigger = trigger;
    }

    protected void executeTask(DBConnection2 dbConnection) throws LoginException,
                                                                  CommException,
                                                                  RemoteException
    {
        debugLog.info("cancelBuyTrigger: start");

        int updateVal = 0;
        String update_qry = "";
        BigDecimal bal;

        userInfo.balance = userInfo.balance.add(trigger.amount);
        userInfo.triggerReserveBalance = userInfo.triggerReserveBalance.subtract(trigger.amount);
        bal = roundForDB(userInfo.balance.doubleValue());

        //delete the trigger row and update the users dollaraccount
        update_qry = "delete from triggers where triggerid = '" + trigger.id + "'";
        update_qry += ";update dollaraccounts set balance = '" + bal + "' where userid = '" + userId + "'";

        debugLog.info("cancelBuyTrigger: qry" + update_qry);
        try {
            updateVal = dbConnection.execUpdate(update_qry);
            this.writeQryCount++;
        } catch (SQLException sqle) {
            debugLog.warn("addBuyTrigger: SQLException", sqle);
            throw new CommException("addBuyTrigger: SQLException" + sqle);
        }
    }

}

class CancelSellTriggerDBTask extends DBTask {

    private int userId;
    private UserInfo userInfo;
    private Trigger trigger;

    public CancelSellTriggerDBTask(int userId, UserInfo userInfo, Trigger trigger) {
        super();
        this.userId = userId;
        this.userInfo = userInfo;
        this.trigger = trigger;
    }

    protected void executeTask(DBConnection2 dbConnection) throws LoginException,
                                                                  CommException,
                                                                  RemoteException
    {
        debugLog.info("cancelSellTrigger: start");

        int updateVal = 0;
        StringBuffer q = new StringBuffer();
        BigDecimal bal;

        StockRecord record = (StockRecord)userInfo.stocks.get(trigger.stock);
        if (record != null) {
            record.amount += trigger.amount;
            record.triggerReserveAmount -= trigger.amount;
        }

        //delete the trigger row and update the users dollaraccount
        q.append("delete from triggers where triggerid = '");
        q.append(trigger.id);
        q.append("'; update ");
        q.append(getStockAccountTable(userInfo));
        q.append(" set stock_balance = '");
        q.append(record.amount);
        q.append("' where userid = '");
        q.append(userId);
        q.append("' and stocksym = '");
        q.append(record.stock);
        q.append("'");

        String qString = q.toString();
        debugLog.info("cancelBuyTrigger: qry " + qString);
        try {
            updateVal = dbConnection.execUpdate(qString);
            this.writeQryCount++;
        } catch (SQLException sqle) {
            debugLog.warn("addBuyTrigger: SQLException", sqle);
            throw new CommException("addBuyTrigger: SQLException" + sqle);
        }
        debugLog.info("cancelSellTrigger: end");
    }

}


class BuyDBTask extends DBTask {
    private int transId;
    private UserInfo userInfo;
    private int userId;
    private PurchaseOrder order;
    private Receipt receipt;

    public BuyDBTask(int transId, int userId, PurchaseOrder order, UserInfo userInfo) {
        super();
        this.transId = transId;
        this.userId = userId;
        this.order = order;
        this.userInfo = userInfo;
    }

    public int getTransId() {
        return transId;
    }

        public PurchaseOrder getPurchaseOrder()
        {
                return order;
        }

    public UserInfo getUserInfo() {
        return userInfo;
    }

    public int getUserId() {
        return userId;
    }

        public Receipt getReceipt()
        {
                return receipt;
        }

    //
    // Executes the buy task, save this result in userid and userInfo member variables,
    // then calls notifyAll to wake up the thread waiting for the task to complete.
    //
    protected void executeTask(DBConnection2 dbConnection) throws LoginException,
                                                                  CommException,
                                                                  RemoteException {

        debugLog.info("Running DBBuy Task...");
        error = null;
        receipt = null;
        int stkBalance = 0;
        StringBuffer q = new StringBuffer();
        String stkSym;
        StockRecord stkRec = null;

        try {

            //calc the new dollar balance (update userinfo)
            double balance = userInfo.balance.doubleValue();
            balance -= order.total.doubleValue();
            BigDecimal bal = roundForDB(balance);

            //check if the user already owns the stock
            stkSym = order.quote.stock;
            stkRec = (StockRecord)userInfo.stocks.get(stkSym);

            if (stkRec == null) { //the user does not yet own this stock

                //add the stock record to the db and add to userinfo
                q.append("insert into ");
                q.append(getStockAccountTable(userInfo));
                q.append(" (userid, stocksym, stock_balance) values ('");
                q.append(userId);
                q.append("','");
                q.append(order.quote.stock);
                q.append("','");
                q.append(order.amount);
                q.append("'); update dollaraccounts set balance = '");
                q.append(bal);
                q.append("' where userid = '");
                q.append(userId);
                q.append("'");

                //add new stock record to userinfo
                debugLog.info("buy: added new stock record");
                userInfo.stocks.put(stkSym, new StockRecord(stkSym, order.amount));

            } else { //the user does own the stock update the balances

                stkBalance = stkRec.amount;
                stkBalance += order.amount;
                stkRec.amount = stkBalance;
                //update the userinfo
                userInfo.stocks.put(stkSym, stkRec);

                q.append("update ");
                q.append(getStockAccountTable(userInfo));
                q.append(" set stock_balance = '");
                q.append(stkBalance);
                q.append("' where userid = '");
                q.append(userId);
                q.append("' and stocksym = '");
                q.append(order.quote.stock);
                q.append("'; update dollaraccounts set balance = '");
                q.append(bal);
                q.append("' where userid = '");
                q.append(userId);
                q.append("'");
            }


            //update the userinfo with the new balance
            userInfo.balance = userInfo.balance.subtract(order.total);

            //update the stock account for the user
            String qString = q.toString();
            debugLog.info("buy: update qry-" + qString);
            int returnVal = dbConnection.execUpdate(qString);
            this.writeQryCount += 2;
        } catch (SQLException sqle) {
            debugLog.warn("buy: SQL Error!", sqle);
        }

        //build receipt
        receipt = new Receipt();
        receipt.confirmation = "xyz";
        receipt.order = new PurchaseOrder(order);
        receipt.balance = userInfo.balance;
    }

}


class SellDBTask extends DBTask {
    private int transId;
    private UserInfo userInfo;
    private int userId;
    private PurchaseOrder order;
    private Receipt receipt;

    public SellDBTask(int transId, int userId, PurchaseOrder order, UserInfo userInfo) {
        super();
        this.transId = transId;
        this.userId = userId;
        this.order = order;
        this.userInfo = userInfo;
    }

    public int getTransId() {
        return transId;
    }

        public PurchaseOrder getPurchaseOrder()
        {
                return order;
        }

    public UserInfo getUserInfo() {
        return userInfo;
    }

    public int getUserId() {
        return userId;
    }

        public Receipt getReceipt()
        {
                return receipt;
        }

    //
    // Executes the buy task, save this result in userid and userInfo member variables,
    // then calls notifyAll to wake up the thread waiting for the task to complete.
    //
    protected void executeTask(DBConnection2 dbConnection) throws LoginException,
                                                                  CommException,
                                                                  RemoteException {

        debugLog.info("Running DBSell Task...");
        error = null;
        receipt = null;

        String stkSym;
        int stkAmount = 0, stkBal = 0;
        BigDecimal bal = null;
        StockRecord stkRec = null;
        StringBuffer q = new StringBuffer();

                //update the stock and dollar balances in the db and update the userInfo to reflect this
        stkSym = order.quote.stock;
        stkAmount = order.amount;

        //calc new stock balance and dollar balance
        stkRec = (StockRecord)userInfo.stocks.get(stkSym);
        if (stkRec == null)
            throw new CommException("no stock record found");

        //userInfo.stocks.remove(stkSym);
        debugLog.info("sell: cur num stock-" + stkRec.amount + "selling-" + stkAmount);
        stkBal = stkRec.amount - stkAmount;
        stkRec.amount = stkBal;

        bal = roundForDB(userInfo.balance);
        bal = bal.add(roundForDB(order.total));
        userInfo.balance = new Money(bal.doubleValue());

        if (stkBal > 0) {
            userInfo.stocks.put(stkSym, stkRec);
            q.append("update ");
            q.append(getStockAccountTable(userInfo));
            q.append(" set stock_balance = '");
            q.append(stkBal);
            q.append("' where userid = '");
            q.append(userId);
            q.append("' and stocksym = '");
            q.append(stkSym);
            q.append("'");
        } else {
            userInfo.stocks.remove(stkSym);
            q.append("delete from ");
            q.append(getStockAccountTable(userInfo));
            q.append(" where userid = '");
            q.append(userId);
            q.append("' and stocksym = '");
            q.append(stkSym);
            q.append("'");
        }

        try {
            //update the stock balance and dollar balance
            q.append(";update dollaraccounts set balance = '");
            q.append(bal);
            q.append("' where userid = '");
            q.append(userId);
            q.append("'");

            String qString = q.toString();
            debugLog.info("sell: query is" + qString);
            int returnVal = dbConnection.execUpdate(qString);
            this.writeQryCount++;
        } catch (SQLException sqle) {
            debugLog.warn("buy: SQL Error!", sqle);
        }

        //throw new UnsupportedOperationException("Not Implemented: sell");
        receipt = new Receipt();
        receipt.confirmation = "sel";
        receipt.order = new PurchaseOrder(order);
        debugLog.info("DBSell: end of sell");
        }

}


class AddFundsDBTask extends DBTask {
    private int transId;
    private UserInfo userInfo;
    private int userId;
    private float amount;
    private Receipt receipt;

    public AddFundsDBTask(int transId, int userId, float amount, UserInfo userInfo) {
        super();
        this.transId = transId;
        this.userId = userId;
        this.amount = amount;
        this.userInfo = userInfo;
    }

    public int getTransId() {
        return transId;
    }

    public float getAmount()
    {
        return amount;
    }

    public UserInfo getUserInfo() {
        return userInfo;
    }

    public int getUserId() {
        return userId;
    }

    public Receipt getReceipt()
    {
        return receipt;
    }

    //
    // Executes the buy task, save this result in userid and userInfo member variables,
    // then calls notifyAll to wake up the thread waiting for the task to complete.
    //
    protected void executeTask(DBConnection2 dbConnection) throws LoginException,
                                                                  CommException,
                                                                  RemoteException {

        debugLog.info("Running DBAddFunds Task...");
        error = null;
        receipt = null;

        userInfo.balance = userInfo.balance.add(amount);

        try {
            BigDecimal bal = roundForDB(userInfo.balance);
            debugLog.info("add: amount formatted" + bal);

            //update the dollaraccount balance
            String qry = "update dollaraccounts set balance = '" + bal + "' where userid = '" + userId + "'";
            int returnVal = dbConnection.execUpdate(qry);
            this.writeQryCount++;
            debugLog.info("add: added funds to balance");

        } catch (SQLException sqle) {
            debugLog.warn("add: SQL Error!", sqle);
        }

        Receipt r = new Receipt();
        r.confirmation = "aaa";
        r.order = new PurchaseOrder(PurchaseOrder.BUY);
        r.order.total = new Money(amount);
        r.order.amount = 1;
        r.balance = userInfo.balance;
        this.receipt = r;
        debugLog.info("add: end of add");

        debugLog.info("DBAddFunds: end of add funds");
        }

}

